/*
 * MenuItem.java  0.4.0 / Oct 26, 2010
 *
 * Copyright (c) 2010 - 2011, cute4j.com and contributors.
 *
 * This software is licensed under the LGPL version 2.1 license.
 * A copy of this license is included in the file LICENSE-LGPL.txt.
 */
package com.cute4j.swing;

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;

import com.cute4j.swing.events.ActionEvent;
import com.cute4j.swing.events.ActionListener;
import com.cute4j.swing.properties.MenuItemProperties;

/**
 * Menu Item Component.
 *
 * @cute4j.native
 *    generate="header"
 *
 * @author Slobodan Vrkacevic
 * @version 0.4.0
 */
public class MenuItem extends Component implements MenuItemProperties {

// Static Initialization. ------------------------------------------------------

  static {
    _initialize();
  }

// Package-Private Fields. -----------------------------------------------------

  String text;
  KeyStroke accelerator;
  SwingIconSet iconSet;
  int mnemonic;
  Action action;
  String actionCommand;
  PropertyChangeListener actionPropertyChangeListener; // NOPMD

// Public Constructors. --------------------------------------------------------

  /**
   * Constructs new MenuItem object.
   */
  public MenuItem() {
    this(null, null);
  }

  /**
   * Constructs new MenuItem object.
   *
   * @param text the text
   */
  public MenuItem(String text) {
    this(text, null);
  }

  /**
   * Constructs new MenuItem object.
   *
   * @param icon the icon
   */
  public MenuItem(Icon icon) {
    this(null, icon);
  }

  /**
   * Constructs new MenuItem object.
   *
   * @param text the text
   * @param icon the icon
   */
  public MenuItem(String text, Icon icon) {
    if (text != null) {
      this.text = text;
      _setText(_delegate, text); // NOPMD
    }
    mnemonic = SwingUtilities.getMnemonicIndex(text);
    if (icon != null) {
      iconSet = new SwingIconSet(this);
      _setIcon(_delegate, iconSet); // NOPMD
      iconSet.setIcon(icon, null);
    }
  }

  /**
   * Constructs new MenuItem object using the specified action.
   *
   * @param action the action
   */
  public MenuItem(Action action) {
    this(null, null);
    setAction(action); // NOPMD
  }

// Public Methods. -------------------------------------------------------------

  /**
   * Returns the parent menu of this menu item.
   *
   * @return the parent menu of this menu item
   */
  public Menu getParentMenu() {
    return (Menu)parent;
  }

  /**
   * Returns text.
   *
   * @return text
   */
  public String getText() {
    return text;
  }

  /**
   * Sets text.
   *
   * @param text the text
   */
  public void setText(String text) {
    final String oldText = this.text;
    if (oldText == null) {
      if (text == null) {
        return;
      }
    } else {
      if (oldText.equals(text)) {
        return;
      }
    }
    disposedCheck();
    _setText(_delegate, text);
    this.text = text;
    firePropertyChange(PROPERTY_TEXT, oldText, text);
  }

  /**
   * Returns the keyboard mnemonic.
   *
   * @return the keyboard mnemonic
   */
  public int getMnemonic() {
    return mnemonic;
  }

  /**
   * Sets the keyboard mnemonic.
   *
   * @param mnemonic the keyboard mnemonic
   */
  public void setMnemonic(int mnemonic) {
    final int oldMnemonic = this.mnemonic;
    if (oldMnemonic != mnemonic) {
      if (mnemonic == -1) {
        if (text != null) {
          _setText(_delegate, text);
          this.mnemonic = mnemonic;
          firePropertyChange(PROPERTY_MNEMONIC, oldMnemonic, mnemonic);
        }
      } else {
        if ((text != null) && (text.length() > mnemonic)) {
          StringBuffer sb = new StringBuffer(text);
          sb.insert(mnemonic, '&');
          _setText(_delegate, sb.toString());
          this.mnemonic = mnemonic;
          firePropertyChange(PROPERTY_MNEMONIC, oldMnemonic, mnemonic);
        }
      }
    }
  }

  /**
   * Returns accelerator.
   *
   * @return accelerator
   */
  public KeyStroke getAccelerator() {
    return accelerator;
  }

  /**
   * Sets accelerator.
   *
   * @param keyStroke the accelerator
   */
  public void setAccelerator(KeyStroke keyStroke) {
    final KeyStroke oldAccelerator = this.accelerator;
    if (oldAccelerator == null) {
      if (keyStroke == null) {
        return;
      }
    } else {
      if (oldAccelerator.equals(keyStroke)) {
        return;
      }
    }
    disposedCheck();
    if (keyStroke == null) {
      _setShortcut(_delegate, null);
    } else {
      _setShortcut(_delegate, keyStroke.shortcut);
    }
    accelerator = keyStroke;
    firePropertyChange(PROPERTY_ACCELERATOR, oldAccelerator, keyStroke);
  }

  /**
   * Returns icon.
   *
   * @return icon
   */
  public Icon getIcon() {
    if (iconSet != null) {
      return iconSet.getIcon();
    }
    return null;
  }

  /**
   * Sets icon.
   *
   * @param icon the icon
   */
  public void setIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setIcon(null, PROPERTY_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setIcon(icon, PROPERTY_ICON);
    }
  }

  /**
   * Returns disabled icon.
   *
   * @return disabled icon
   */
  public Icon getDisabledIcon() {
    if (iconSet != null) {
      return iconSet.getDisabledIcon();
    }
    return null;
  }

  /**
   * Sets disabled icon.
   *
   * @param icon the disabled icon
   */
  public void setDisabledIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setDisabledIcon(null, PROPERTY_DISABLED_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setDisabledIcon(icon, PROPERTY_DISABLED_ICON);
    }
  }

  /**
   * Returns rollover icon.
   *
   * @return rollover icon
   */
  public Icon getRolloverIcon() {
    if (iconSet != null) {
      return iconSet.getRolloverIcon();
    }
    return null;
  }

  /**
   * Sets rollover icon.
   *
   * @param icon the rollover icon
   */
  public void setRolloverIcon(Icon icon) {
    disposedCheck();
    if (icon == null) {
      if (iconSet != null) {
        iconSet.setRolloverIcon(null, PROPERTY_ROLLOVER_ICON);
        if (iconSet.isEmpty()) {
          _setIcon(_delegate, null);
          iconSet = null;
        }
      }
    } else {
      if (iconSet == null) {
        iconSet = new SwingIconSet(this);
        _setIcon(_delegate, iconSet);
      }
      iconSet.setRolloverIcon(icon, PROPERTY_ROLLOVER_ICON);
    }
  }

  /**
   * Returns action.
   *
   * @return action
   */
  public Action getAction() {
    return action;
  }

  /**
   * Sets the action.
   *
   * @param action the action
   */
  public void setAction(Action action) {
    final Action oldAction = this.action;
    if (oldAction == null) {
      if (action == null) {
        return;
      }
    } else {
      if (oldAction.equals(action)) {
        return;
      }
    }
    disposedCheck();
    if (oldAction != null) {
      removeActionListener(oldAction);
      oldAction.removePropertyChangeListener(actionPropertyChangeListener);
      actionPropertyChangeListener = null;
    }
    configurePropertiesFromAction(action);
    if (action != null) {
      if (!listenerList.containsListener(ActionListener.class, action)) {
        addActionListener(action);
      }
      actionPropertyChangeListener = createActionPropertyChangeListener();
      action.addPropertyChangeListener(actionPropertyChangeListener);
    }
    this.action = action;
    firePropertyChange(PROPERTY_ACTION, oldAction, action);
  }

  /**
   * Returns action command.
   *
   * @return action command
   */
  public String getActionCommand() {
    if (actionCommand == null) {
      return text;
    }
    return actionCommand;
  }

  /**
   * Sets action command for this button.
   *
   * @param actionCommand the action command
   */
  public void setActionCommand(String actionCommand) {
    final String oldActionCommand = this.actionCommand;
    if (oldActionCommand == null) {
      if (actionCommand == null) {
        return;
      }
    } else {
      if (oldActionCommand.equals(actionCommand)) {
        return;
      }
    }
    this.actionCommand = actionCommand;
    firePropertyChange(PROPERTY_ACTION_COMMAND, oldActionCommand,
                       actionCommand);
  }

  /**
   * Adds action listener.
   *
   * @param listener the action listener.
   */
  public void addActionListener(ActionListener listener) {
    listenerList.add(ActionListener.class, listener);
  }

  /**
   * Removes action listener.
   *
   * @param listener the action listener.
   */
  public void removeActionListener(ActionListener listener) {
    listenerList.remove(ActionListener.class, listener);
  }

  /**
   * Returns all action listeners.
   *
   * @return the action listener array
   */
  public ActionListener[] getActionListeners() {
    int count = listenerList.getListenerCount(ActionListener.class);
    ActionListener[] listeners = new ActionListener[count];
    if (count > 0) {
      System.arraycopy(listenerList.getListenersArray(ActionListener.class), 0,
                       listeners,  0, count);
    }
    return listeners;
  }

// Protected Methods. ----------------------------------------------------------

  /**
   * Configures action properties from the specified action.
   *
   * @param action the action
   */
  protected void configurePropertiesFromAction(Action action) {
    if (action != null) {
      setText((String)action.getValue(Action.NAME));
      Integer mnemonic = (Integer)action.getValue(AbstractAction.MNEMONIC_KEY);
      if (mnemonic != null) {
        setMnemonic(mnemonic.intValue());
      }
      setAccelerator((KeyStroke)action.getValue(Action.ACCELERATOR_KEY));
      setToolTipText((String)action.getValue(Action.SHORT_DESCRIPTION));
      setStatusTipText((String)action.getValue(Action.LONG_DESCRIPTION));
      setWhatsThisText((String)action.getValue(Action.HELP_DESCRIPTION));
      setIcon((Icon)action.getValue(Action.SMALL_ICON));
      setEnabled(action.isEnabled());
    }
  }

  /**
   * Creates and returns PropertyChangeListener for menu item action.
   *
   * @return PropertyChangeListener for menu item action
   */
  protected PropertyChangeListener createActionPropertyChangeListener() {
    return new ActionPropertyChangeListener(this);
  }

  /**
   * Fires action event.
   *
   * @param e the action event
   */
  protected void fireActionPerformed(ActionEvent e) {
    int count = listenerList.getListenerCount(ActionListener.class);
    if (count > 0) {
      ActionListener[] listeners = (ActionListener[])listenerList.
                                       getListenersArray(ActionListener.class);
      for (int i = count; --i >= 0;) {
        listeners[i].actionPerformed(e);
      }
    }
  }

// Overridden Methods. ---------------------------------------------------------

  /**
   * {@inheritDoc}
   */
  public void updateLocalization() {
    super.updateLocalization();
    if (text != null) {
      _setText(_delegate, localizeString(text));
    }
  }

  /**
   * {@inheritDoc}
   */
  protected String paramString() {
    StringBuffer sb = new StringBuffer(super.paramString());
    if (text != null) {
      sb.append(",text=");
      sb.append(text);
    }
    return sb.toString();
  }

// Native Callbacks. -----------------------------------------------------------

  void _actionTriggered(int modifiers) {
    fireActionPerformed(new ActionEvent(this, ActionEvent.ACTION_PERFORMED,
                                        getActionCommand(),
                                        System.currentTimeMillis(), modifiers));
  }

// Package-Private Native Methods. ---------------------------------------------

  native long _create();

  native void _setText(long delegate, String text);

  native void _setShortcut(long delegate, String shortcut);

  native void _setIcon(long delegate, SwingIconSet iconSet);

// Private Static Native Methods. ----------------------------------------------

  private static native void _initialize();

// Inner Classes. --------------------------------------------------------------

  /**
   * Action PropertyChangeListener.
   */
  protected static class ActionPropertyChangeListener implements
                                                       PropertyChangeListener {
    protected MenuItem menuItem;

    /**
     * Constructs new ActionPropertyChangeListener object.
     *
     * @param menuItem the menu item
     */
    public ActionPropertyChangeListener(MenuItem menuItem) {
      this.menuItem = menuItem;
    }

    /**
     * {@inheritDoc}
     */
    public void propertyChange(PropertyChangeEvent e) {
      Action action = (Action)e.getSource();
      String propertyName = e.getPropertyName();
      if (AbstractAction.PROPERTY_ENABLED.equals(propertyName)) {
        menuItem.setEnabled(action.isEnabled());
      } else if (AbstractAction.NAME.equals(propertyName)) {
        menuItem.setText((String)action.getValue(AbstractAction.NAME));
      } else if (AbstractAction.SMALL_ICON.equals(propertyName)) {
        menuItem.setIcon((Icon)action.getValue(AbstractAction.SMALL_ICON));
      } else if (AbstractAction.SHORT_DESCRIPTION.equals(propertyName)) {
        menuItem.setToolTipText((String)action.getValue(
                                            AbstractAction.SHORT_DESCRIPTION));
      } else if (AbstractAction.MNEMONIC_KEY.equals(propertyName)) {
        Integer mnemonic = (Integer)action.getValue(
                                                  AbstractAction.MNEMONIC_KEY);
        if (mnemonic != null) {
          menuItem.setMnemonic(mnemonic.intValue());
        }
      }
    }

  }

}
